package com.linkoog.devtools.json.file_editer.editer.ui;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.ide.structureView.StructureViewModel;
import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
import com.intellij.json.psi.JsonArray;
import com.intellij.json.psi.JsonContainer;
import com.intellij.json.psi.JsonObject;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.JsonValue;
import com.intellij.json.structureView.JsonStructureViewModel;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.editor.ScrollingModel;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.treeStructure.treetable.TreeTableTree;
import com.linkoog.devtools.json.file_editer.editer.ui.jsonTree.JsonStructureTree;
import com.linkoog.devtools.json.file_editer.editer.ui.jsonTreeTable.JsonStructureTreeTable;
import com.linkoog.devtools.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class JsonEditerRightPanel {

    private final Project project;
    private final TextEditor textEditor;
    private final EditorEx editor;
    @NotNull
    private final PsiFile psiFile;

    private JPanel myMainPanel;

    private Consumer<String> resetCallback;


    public JsonEditerRightPanel(Project project, TextEditor textEditor) {
        this.project = project;
        this.textEditor = textEditor;
        this.editor = (EditorEx) textEditor.getEditor();
        Document document = editor.getDocument();
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
        this.psiFile = psiDocumentManager.getPsiFile(document);

        myMainPanel.setMinimumSize(new Dimension(180,-1));

        JComponent jsonTreeScrollPane = createLeftPanel_Tree();
        //JComponent jsonTreeScrollPane = createLeftPanel_TreeTable();
        //JComponent jsonTreeScrollPane = createLeftPanel_StructureView();
        if (jsonTreeScrollPane != null){
            myMainPanel.add(jsonTreeScrollPane, BorderLayout.CENTER);
        }
    }

    /**
     *   创建自定义 tree 形式的导航
     */
    private JComponent createLeftPanel_Tree() {
        JsonStructureTree structureTree = new JsonStructureTree();

        JBScrollPane jsonTreeScrollPane = new JBScrollPane();
        jsonTreeScrollPane.getViewport().add(structureTree);
        jsonTreeScrollPane.setBorder(BorderFactory.createEmptyBorder());

        /*int startOffset = 10;  // 选中文本的起始偏移量
        int endOffset = 20;    // 选中文本的结束偏移量
        editor.getSelectionModel().setSelection(startOffset, endOffset);*/

        structureTree.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                JackJsonTreeNode select = (JackJsonTreeNode)structureTree.getLastSelectedPathComponent();

                if (select != null){
                    int clickCount = e.getClickCount();
                    if (clickCount == 1) {
                        //双击
                        List<JackJsonTreeNode> paths = getNodePath(select);
                        scrollToText(paths);
                    }
                }
            }
        });

        resetCallback = structureTree::reset;
        return jsonTreeScrollPane;
    }

    /**
     *   创建 treeTable 形式的导航
     */
    private JComponent createLeftPanel_TreeTable() {
        JsonStructureTreeTable jsonTreeTableView = new JsonStructureTreeTable();

        JBScrollPane jsonTreeScrollPane = new JBScrollPane();
        jsonTreeScrollPane.getViewport().add(jsonTreeTableView);
        jsonTreeScrollPane.setBorder(BorderFactory.createEmptyBorder());

        /*int startOffset = 10;  // 选中文本的起始偏移量
        int endOffset = 20;    // 选中文本的结束偏移量
        editor.getSelectionModel().setSelection(startOffset, endOffset);*/

        jsonTreeTableView.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                TreeTableTree tree = jsonTreeTableView.getTree();
                JackJsonTreeNode select = (JackJsonTreeNode)tree.getLastSelectedPathComponent();

                if (select != null){
                    int clickCount = e.getClickCount();
                    if (clickCount == 1) {
                        //双击
                        List<JackJsonTreeNode> paths = getNodePath(select);
                        scrollToText(paths);
                    }
                }
            }
        });

        resetCallback = jsonTreeTableView::reset;
        return jsonTreeScrollPane;
    }

    /**
     *   创建 Structure 形式的导航
     */
    private JComponent createLeftPanel_StructureView() {
        /*PsiStructureViewFactory factory = LanguageStructureViewBuilder.getInstance().forLanguage(Json5Language.INSTANCE);
        if (factory == null) return null;
        StructureViewBuilder builder = factory.getStructureViewBuilder(psiFile);
        if (builder == null) return null;*/

        StructureViewBuilder builder = new TreeBasedStructureViewBuilder() {
            @Override
            public @NotNull StructureViewModel createStructureViewModel(@Nullable Editor editor) {
                return new JsonStructureViewModel(psiFile, editor);
            }
        };

        return builder.createStructureView(textEditor, project).getComponent();
    }




    private List<JackJsonTreeNode> getNodePath(JackJsonTreeNode node){
        List<JackJsonTreeNode> result = new ArrayList<>();
        recursiveNodePath(node, result);
        return result;
    }

    private void recursiveNodePath(JackJsonTreeNode node, List<JackJsonTreeNode> result){
        result.add(0, node);
        JackJsonTreeNode parent = (JackJsonTreeNode)node.getParent();
        if (! node.isRoot() && parent != null){
            recursiveNodePath(parent, result);
        }
    }

    public void reset(String json){
        if (resetCallback != null){
            resetCallback.accept(json);
        }
    }

    public void scrollToText(final List<JackJsonTreeNode> path) {
        PsiElement[] content = this.psiFile.getChildren();
        Arrays.stream(content)
                .filter(ele -> StringUtils.isNotEmpty(ele.getText()))
                .filter(ele -> ele instanceof JsonContainer)
                .findFirst()
                .ifPresent(psiElement -> scrollToText(path, psiElement));
    }

    private void scrollToText(List<JackJsonTreeNode> path, PsiElement psiElement){
        JsonValue psiJsonValue = (JsonValue)psiElement;
        if (path.size() == 1) {
            this.scrollToSelection(psiJsonValue.getTextRange());
            return;
        }

        final Iterator<JackJsonTreeNode> it = path.iterator();
        while (it.hasNext()) {
            JackJsonTreeNode node = it.next();
            if (node.isRoot()) {
                continue;
            }

            if (psiJsonValue == null) {
                continue;
            }

            JackJsonTreeNode parent = (JackJsonTreeNode)node.getParent();
            JsonNode parentJacksonNode = parent.getJsonNode();
            int index = parent.getIndex(node);
            if (parentJacksonNode instanceof ObjectNode && psiJsonValue instanceof JsonObject) {
                final JsonObject obj = (JsonObject)psiJsonValue;
                final List<JsonProperty> propertyList = obj.getPropertyList();
                final JsonProperty property = propertyList.stream().filter(p -> node.getName().equals(p.getName())).findFirst().orElse(null);
                if (property != null) {
                    psiJsonValue = property.getValue();
                    if (!it.hasNext()) {
                        this.scrollToSelection(property.getTextRange());
                    }
                }
            } else if (parentJacksonNode instanceof ArrayNode && psiJsonValue instanceof JsonArray) {
                final JsonArray arr = (JsonArray)psiJsonValue;
                final List<JsonValue> valueList = arr.getValueList();
                if (valueList.size() > index) {
                    psiJsonValue = valueList.get(index);
                    if (!it.hasNext()) {
                        this.scrollToSelection(psiJsonValue.getTextRange());
                    }
                }
            }
        }
    }

    private void scrollToSelection(final TextRange range) {
        SelectionModel selectionModel = this.editor.getSelectionModel();
        selectionModel.setSelection(range.getStartOffset(), range.getEndOffset());
        ScrollingModel scrollingModel = this.editor.getScrollingModel();
        CaretModel caretModel = this.editor.getCaretModel();
        caretModel.moveToOffset(range.getStartOffset());
        scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE);
    }

    public JPanel getMyMainPanel() {
        return myMainPanel;
    }
}
